#include "../include/DBUtil.hpp"
#include <mysql/errmsg.h>
#include <stdexcept>
#include <iostream>

int DBUtil::execute(const string& sql) {
	auto conn = ConnectionPool::getInstance()->getConnection();
	if (!conn) return -1;
	// 然后就执行sql
	// int mysql_query(MYSQL *mysql, const char *q) 所以string --> char*
	if (mysql_query(conn.get(), sql.c_str()) != 0) return -1;

	return mysql_affected_rows(conn.get());
}

// 返回Json对象的sql查询
Json::Value DBUtil::query(const string& sql) {
	Json::Value ans;
	auto conn = ConnectionPool::getInstance()->getConnection();
	if (!conn) return ans; // 此处的ans是空的Json

	// sql 语句执行
	if (mysql_query(conn.get(), sql.c_str()) == 0) {
		// 成功就回Json对象对应的结果集
		MYSQL_RES* res = mysql_store_result(conn.get()); // 结果集
		if (res) {
			int fields = mysql_num_fields(res); // 字段数
			MYSQL_FIELD* fieldInfo = mysql_fetch_field(res); // 字段信息

			// row 的形式进结果集
			MYSQL_ROW row;
			while ((row = mysql_fetch_row(res))) {
				Json::Value item;
				for (int i = 0; i < fields; i++) {
					// 字段为空 'NULL'
					const char* val = row[i] ? row[i] : "NULL";
					item[fieldInfo[i].name] = val;
				}
				// append 方法将row记录进结果集
				ans.append(item);
			}
			mysql_free_result(res); // 结果集要释放
		}
	}
	return ans;
}

// 事务类 中要嵌套作用域
DBUtil::Transaction::Transaction() {
	// 事务操作 要将自动提交修改  成为手动提交 便于事务操作
	conn_ = ConnectionPool::getInstance()->getConnection();
	// 事务禁止提交修改成功 0 == 0
	// 1 --> 活跃
	// 0 -->
	active_ = (conn_ && mysql_autocommit(conn_.get(), false) == 0);
}

DBUtil::Transaction::~Transaction() {
	// 需要析构的时候 提交or回滚
	if (active_) {
		// 活跃状态就回滚
		rollback();
	}
}

bool DBUtil::Transaction::commit() {
	// 不活跃就不提交
	if (!active_) return false;
	active_ = false;
	// 手动提交
	return mysql_commit(conn_.get()) == 0;
}

void DBUtil::Transaction::rollback() {
	if (!active_) return;
	active_ = false;
	// 回滚
	mysql_rollback(conn_.get());
}

bool DBUtil::Transaction::execute(const string& sql) {
	if (!active_) return false;
	return mysql_query(conn_.get(), sql.c_str()) == 0;
}

Json::Value DBUtil::Transaction::query(const string& sql) {
	Json::Value ans;
	if (!active_) return ans;

	// sql 语句执行
	// 事务中的用private的字段
	if (mysql_query(conn_.get(), sql.c_str()) == 0) {
		// 成功就回Json对象对应的结果集
		MYSQL_RES* res = mysql_store_result(conn_.get()); // 结果集
		if (res) {
			int fields = mysql_num_fields(res); // 字段数
			MYSQL_FIELD* fieldInfo = mysql_fetch_field(res); // 字段信息

			// row 的形式进结果集
			MYSQL_ROW row;
			while ((row = mysql_fetch_row(res))) {
				Json::Value item;
				for (int i = 0; i < fields; i++) {
					// 字段为空 'NULL'
					const char* val = row[i] ? row[i] : "NULL";
					item[fieldInfo[i].name] = val;
				}
				// append 方法将row记录进结果集
				ans.append(item);
			}
			mysql_free_result(res); // 结果集要释放
		}
	}
	return ans;
}

// 预处理包装类
// 构造
DBUtil::Statement::Statement(MYSQL* conn, const string& sql) {
	// 传进来的conn 要初始化 对字段中的 句柄也初始化
	stmt_ = mysql_stmt_init(conn);
	if (!stmt_) throw runtime_error("mysql_stmt_init 失败");

	if (mysql_stmt_prepare(stmt_, sql.c_str(), sql.length()) != 0) {
		// 调用 预处理准备函数 失败就抛 错误信息
		throw runtime_error(string("mysql_stmt_prepare 失败: ") + mysql_stmt_error(stmt_));
	}
	// 上述分支都没进去则就是 初始化成功
	cout << "预处理语句初始化 成功准备SQL语句" << endl;
}

DBUtil::Statement::~Statement() {
	if (stmt_) mysql_stmt_close(stmt_); // 有预处理句柄 才需要释放  不然容易对没有的进行释放容易出错误
	cout << "释放预处理语句资源" << endl;
}

/**
 * @brief 多个参数进处理语句
 * @param args 参数列表 可变参数 使用模板类可以更通配
 */
template<typename... Args>
void DBUtil::Statement::bindParams(Args&&... args) {
	// private 中的help函数 来处理绑定 接口不暴露处理
	// index 从 0 开始
	bindParamsHelp(0, forward<Args>(args)...);
}

/**
 * @brief 执行预处理语句
 * 多种操作语句 封装在一个函数
 * @return affected rows
 */
int DBUtil::Statement::execute() {
	// 执行预处理语句
	if (mysql_stmt_execute(stmt_) != 0) {
		// 失败就 抛异常打印错误信息
		throw runtime_error(string("mysql_stmt_execute 失败: ") + mysql_stmt_error(stmt_));
	}
	// @return int 返回值是 受影响行数
	cout << "成功执行预处理语句，影响行数: " << mysql_stmt_affected_rows(stmt_) << endl;
	return mysql_stmt_affected_rows(stmt_);
}

/**
 * @brief 查询结果 --> Json对象对应的数据内容
 * @return Json对象的数据
 */
Json::Value DBUtil::Statement::query() {
	// 注意 返回值 
	Json::Value ans;
	// 执行预处理语句
	if (mysql_stmt_execute(stmt_) != 0) {
		// 失败就 抛异常打印错误信息
		throw runtime_error(string("mysql_stmt_execute 失败: ") + mysql_stmt_error(stmt_));
	}
	// @return Json::Value 返回值是 Json 格式的内容
	// 获取结果集的字段信息  元数据 --> result_metadata
	MYSQL_RES* meta = mysql_stmt_result_metadata(stmt_);
	// 没有就给空JSON
	if (!meta) return ans; // 此处的ans 是 nullptr

	// 字段数
	int num_fields = mysql_num_fields(meta);

	// vector 存储结果  绑定信息 数据缓冲区
	vector<MYSQL_BIND> binds(num_fields);
	vector<char*> buf(num_fields);
	vector<unsigned long> len(num_fields);
	// vector<my_bool> is_null(num_fields);
	// my_bool 是 c 库中对bool的定义 此处cpp文件可以直接使用bool
	vector<char> is_null(num_fields);

	// 字段 绑定信息的初始化
	for (int i = 0; i < num_fields; i++) {
		MYSQL_FIELD* field = mysql_fetch_field_direct(meta, i);
		// 给缓冲区
		buf[i] = new char[field->max_length + 1];
		// 每个字段给了以后都刷新一下 避免缓冲区阻塞了导致错误
		memset(buf[i], 0, field->max_length + 1);

		// 给绑定信息
		binds[i].buffer_type = field->type;
		binds[i].buffer = buf[i];
		binds[i].buffer_length = field->max_length;
		// 一定要给引用
		binds[i].length = &len[i];
		// std::vector<bool>的特殊实现方式上。在C++中，std::vector<bool>是一个特化版本，它使用位压缩存储，因此不能直接获取其元素的地址。
		binds[i].is_null = reinterpret_cast<bool*>(&is_null[i]);
	}

	// 绑定信息与预处理语句关联
	if (mysql_stmt_bind_result(stmt_, binds.data()) != 0) {
		// 元数据释放
		mysql_free_result(meta);
		// 迭代释放每一个缓冲区
		for (auto buff : buf) delete[] buff;

		// 绑定失败 抛异常 给错误提醒
		throw runtime_error(string("mysql_stmt_bind_result 失败: ") + mysql_stmt_error(stmt_));
	}

	// 遍历结果集 获取查询结果
	while (mysql_stmt_fetch(stmt_) == 0) {
		Json::Value row;
		for (int i = 0; i < num_fields; i++) {
			MYSQL_FIELD* field = mysql_fetch_field_direct(meta, i);
			// 字段为 null JSON对应也为 null
			if (is_null[i]) row[field->name] = Json::nullValue;
			// 字段的val传进Json 以string 类型
			else row[field->name] = string(buf[i], len[i]);
		}
		// 每一行row的合并成一整个JSON数组
		ans.append(row);
	}

	// 资源记得关闭
	mysql_free_result(meta);
	for (auto buff : buf) delete[] buff;
	cout << "查询结果处理完毕" << endl;

	return ans;
}

// 参数绑定的递归终止条件
void DBUtil::Statement::bindParamsHelp(int) {}

// 模板类 便于对应参数列表 更强的适配性
// 递归实现 参数绑定
template<typename T, typename... Args>
void DBUtil::Statement::bindParamsHelp(int index, T&& val, Args&&... args) {
	MYSQL_BIND bind{};
	// 设置绑定值
	setBindValue(bind, forward<T>(val));
	// 绑定
	if (mysql_stmt_bind_param(stmt_, &bind) != 0)
		throw runtime_error(string("mysql_stmt_bind_param 失败: ") + mysql_stmt_error(stmt_));
	cout << "绑定第 " << index << " 个参数，值: " << endl;

	// 递归剩余的
	bindParamsHelp(index + 1, forward<Args>(args)...);
}

// 不同类型的 bind 多种均可适配在args中
// 可能是 (varchar 或者 int)
void DBUtil::Statement::setBindValue(MYSQL_BIND& bind, int val) {
	bind.buffer_type = MYSQL_TYPE_LONG;
	bind.buffer = &val;
	bind.is_null = 0;
	bind.length = 0;
	cout << "设置绑定值，类型: 整数（int），值: " << val << endl;
}

void DBUtil::Statement::setBindValue(MYSQL_BIND& bind, const string& val) {
	bind.buffer_type = MYSQL_TYPE_STRING;
	bind.buffer = const_cast<char*>(val.c_str());
	bind.is_null = 0;
	bind.length = 0;
	cout << "设置绑定值，类型: 字符串（string），值: " << val << endl;
}
